package ${vhGrpcBuilder_packageName}.svc.${vhGrpcBuilder_sprojectName};

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;

import org.apache.http.client.HttpClient;
import org.apache.http.config.Registry;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.client.RestTemplate;

import com.viewhigh.vhsc.message.MessageConfig;
import com.viewhigh.vhsc.metrics.EnableVhscMetrics;
import com.viewhigh.vhsc.metrics.EndpointMode;
import com.viewhigh.vhsc.metrics.grpc.MonitoringServerInterceptor;
import com.viewhigh.vhsc.support.grpc.GrpcServerTraceInterceptor;
import com.viewhigh.vhsc.support.grpc.SmartGrpcServiceDiscoverer;
import com.viewhigh.vhsc.tracing.TracerFactory;
import com.viewhigh.vhsc.tracing.grpc.ServerTracingInterceptor;

import io.opentracing.Tracer;
import net.devh.springboot.autoconfigure.grpc.server.GlobalServerInterceptorConfigurerAdapter;
import net.devh.springboot.autoconfigure.grpc.server.GlobalServerInterceptorRegistry;

@SpringBootApplication
@MapperScan({"${vhGrpcBuilder_packageName}.svc.${vhGrpcBuilder_sprojectName}.mapper", "com.viewhigh.vhsc.support.api3.mapper"})
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan({"${vhGrpcBuilder_packageName}.svc.${vhGrpcBuilder_sprojectName}", "com.viewhigh.vhsc.support.api3"})
@Import(value={MessageConfig.class})
@EnableTransactionManagement
@EnableVhscMetrics(mode = EndpointMode.SIMPLE_HTTP_SERVER)
public class ${vhGrpcBuilder_projectName}SvcApplication {
  
	public static void main(String[] args) {
		SpringApplication.run(${vhGrpcBuilder_projectName}SvcApplication.class, args);
	}
	
	@Bean
	public SmartGrpcServiceDiscoverer defaultGrpcServiceFinder() {
		return new SmartGrpcServiceDiscoverer();
	}
	
	@Bean
	public Tracer tracer() {
		TracerFactory tracerFactory = new TracerFactory("${vhGrpcBuilder_firstName}-${vhGrpcBuilder_sprojectName}-svc");
		return tracerFactory.getTracer();
	}

	@Bean
	public StringRedisTemplate stringRedisTemplate(JedisConnectionFactory jedisConnectionFactory) {
		return new StringRedisTemplate(jedisConnectionFactory);
	}

	@Bean
	public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
		RedisTemplate<String, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(jedisConnectionFactory);
		template.setKeySerializer(new StringRedisSerializer());
		template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
		return template;
	}
	
	@Bean
	public GlobalServerInterceptorConfigurerAdapter globalInterceptorConfigurerAdapter() {
		return new GlobalServerInterceptorConfigurerAdapter() {
			@Override
			public void addServerInterceptors(GlobalServerInterceptorRegistry registry) {
				registry.addServerInterceptors(new GrpcServerTraceInterceptor());
				registry.addServerInterceptors(new ServerTracingInterceptor(tracer()));
				registry.addServerInterceptors(MonitoringServerInterceptor.create());
			}
		};
	}
	
	/*
	 * for api3 caller
	 */
	@Bean
	public ClientHttpRequestFactory httpRequestFactory() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
		return new HttpComponentsClientHttpRequestFactory(httpClient());
	}
	@Bean(name = "api3RestTemplate")
	public RestTemplate restTemplate() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
		return new RestTemplate(httpRequestFactory());
	}
	@Bean
	public HttpClient httpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
		TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
		SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
				.loadTrustMaterial(null, acceptingTrustStrategy)
				.build();
		SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new HostnameVerifier() {
			@Override
			public boolean verify(String s, SSLSession sslSession) {
				return true;
			}
		});
		Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
				.register("http", PlainConnectionSocketFactory.getSocketFactory())
				.register("https", csf)
				.build();
		PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
		connectionManager.setMaxTotal(5);
		connectionManager.setDefaultMaxPerRoute(5);

		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(61000)
				.setConnectTimeout(8000)
				.setConnectionRequestTimeout(8000)
				.build();

		return HttpClientBuilder.create()
				.setDefaultRequestConfig(requestConfig)
				.setConnectionManager(connectionManager)
				.build();
	}
}
